package de.hauschild.dbc4j.aspects;

import java.lang.reflect.Method;
import java.util.Set;

import org.apache.commons.lang.text.StrBuilder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import de.hauschild.dbc4j.ContractViolationException;
import de.hauschild.dbc4j.MalformedContractException;
import de.hauschild.dbc4j.annotation.Contracted;
import de.hauschild.dbc4j.annotation.PreCondition;
import de.hauschild.dbc4j.engine.ContractEngine;
import de.hauschild.dbc4j.engine.ContractEngineUtils;
import de.hauschild.dbc4j.util.JoinPointUtils;

/**
 * The aspect matching and evaluating all {@link PreCondition PreConditions} at a {@link Contracted} class.
 * 
 * @since 1.0.0
 * @author Klaus Hauschild
 */
@Aspect
public class PreConditionAspect extends AbstractConditionAspect {

  private PreConditionAspect() {
  }

  // @Before("(execution (((@de.hauschild.dbc4j.annotation.Contracted *)+).new(..)))")
  public void constructors(final JoinPoint joinPoint) throws ContractViolationException {
    // TODO implement
  }

  @Before("execution (* ((@de.hauschild.dbc4j.annotation.Contracted *)+).*(..))")
  public void methods(final JoinPoint joinPoint) throws ContractViolationException, MalformedContractException {
    // get method
    final Method method = JoinPointUtils.getMethod(joinPoint);
    // get preconditions for method
    final Set<PreCondition> preConditions = getAnnotationFrom(method, PreCondition.class);
    // check each precondition
    for (final PreCondition preCondition : preConditions) {
      // determine condition engine
      final ContractEngine engine = ContractEngineUtils.getContractEngine(preCondition);
      // evaluate precondition
      if (!engine.evaluate(preCondition, joinPoint.getTarget(), joinPoint.getArgs())) {
        // condition violated -> exception
        throw new ContractViolationException(String.format(
            "The precondition '%s' was violated at %s (%s) with arguments: %s.", preCondition.value(), joinPoint
                .getTarget(), method, new StrBuilder().appendWithSeparators(joinPoint.getArgs(), ", ")));
      }
      // contract hold
    }
  }

  public void staticMethods(final JoinPoint joinPoint) throws ContractViolationException {
    // TODO implement
  }

}
